/**
 * ***************************************************************************** Copyright (c) 2003,
 * 2006 Subclipse project and others. All rights reserved. This program and the accompanying
 * materials are made available under the terms of the Eclipse Public License v1.0 which accompanies
 * this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
 *
 * <p>Contributors: Subclipse project committers - initial API and implementation
 * ****************************************************************************
 */
package org.tigris.subversion.subclipse.core.resources;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.core.TeamException;
import org.tigris.subversion.subclipse.core.ISVNFolder;
import org.tigris.subversion.subclipse.core.ISVNRemoteFolder;
import org.tigris.subversion.subclipse.core.ISVNRemoteResource;
import org.tigris.subversion.subclipse.core.ISVNRepositoryLocation;
import org.tigris.subversion.subclipse.core.ISVNResource;
import org.tigris.subversion.subclipse.core.Policy;
import org.tigris.subversion.subclipse.core.SVNException;
import org.tigris.subversion.subclipse.core.SVNProviderPlugin;
import org.tigris.subversion.subclipse.core.SVNStatus;
import org.tigris.subversion.svnclientadapter.ISVNClientAdapter;
import org.tigris.subversion.svnclientadapter.ISVNDirEntry;
import org.tigris.subversion.svnclientadapter.ISVNDirEntryWithLock;
import org.tigris.subversion.svnclientadapter.ISVNLock;
import org.tigris.subversion.svnclientadapter.SVNClientException;
import org.tigris.subversion.svnclientadapter.SVNNodeKind;
import org.tigris.subversion.svnclientadapter.SVNRevision;
import org.tigris.subversion.svnclientadapter.SVNUrl;

/** This class provides the implementation of ISVNRemoteFolder */
public class RemoteFolder extends RemoteResource implements ISVNRemoteFolder, ISVNFolder {

  protected ISVNRemoteResource[] children;

  /**
   * Constructor for RemoteFolder.
   *
   * @param parent
   * @param repository
   * @param url
   * @param revision
   * @param lastChangedRevision
   * @param date
   * @param author
   */
  public RemoteFolder(
      RemoteFolder parent,
      ISVNRepositoryLocation repository,
      SVNUrl url,
      SVNRevision revision,
      SVNRevision.Number lastChangedRevision,
      Date date,
      String author) {
    super(parent, repository, url, revision, lastChangedRevision, date, author);
  }

  /**
   * Constructor (from url and revision)
   *
   * @param repository
   * @param url
   * @param revision
   */
  public RemoteFolder(ISVNRepositoryLocation repository, SVNUrl url, SVNRevision revision) {
    super(repository, url, revision);
  }

  /**
   * Constructor (from byet[])
   *
   * @param resource
   * @param bytes
   */
  public RemoteFolder(IResource resource, byte[] bytes) {
    super(resource, bytes);
  }

  public RemoteFolder(RemoteResourceStatus remoteStatusInfo) {
    this(
        null,
        remoteStatusInfo.getRepository(),
        remoteStatusInfo.getUrl(),
        remoteStatusInfo.getRepositoryRevision(),
        remoteStatusInfo.getLastChangedRevision(),
        remoteStatusInfo.getLastChangedDate(),
        remoteStatusInfo.getLastCommitAuthor());
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNRemoteResource#exists(org.eclipse.core.runtime.IProgressMonitor)
   */
  public boolean exists(IProgressMonitor monitor) throws TeamException {
    try {
      getMembers(monitor);
      return true;
    } catch (SVNException e) {
      if (e.getStatus().getCode() == SVNStatus.DOES_NOT_EXIST) {
        return false;
      } else {
        throw e;
      }
    }
  }

  /**
   * Check whether the given child exists
   *
   * @param child the child resource to check for existence
   * @param monitor a progress monitor
   * @return true when the child resource exists
   */
  protected boolean exists(final ISVNRemoteResource child, IProgressMonitor monitor)
      throws SVNException {
    ISVNRemoteResource[] members;
    try {
      members = getMembers(monitor);
    } catch (SVNException e) {
      if (e.getStatus().getCode() == SVNStatus.DOES_NOT_EXIST) {
        return false;
      } else {
        throw e;
      }
    }

    for (int i = 0; i < members.length; i++) {
      if (members[i].equals(child)) return true;
    }
    return false;
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNRemoteFolder#refresh()
   */
  public void refresh() {
    children = null;
  }

  /**
   * Get the members of this folder at the same revision than this resource
   *
   * @param monitor a progress monitor
   * @return ISVNRemoteResource[] an array of child remoteResources
   */
  protected ISVNRemoteResource[] getMembers(IProgressMonitor monitor) throws SVNException {

    final IProgressMonitor progress = Policy.monitorFor(monitor);
    progress.beginTask(Policy.bind("RemoteFolder.getMembers"), 100); // $NON-NLS-1$

    // Try to hit the cache first.
    if (children != null) {
      progress.done();
      return children;
    }
    ISVNClientAdapter client = null;
    try {
      client = getRepository().getSVNClient();

      ISVNDirEntryWithLock[] list =
          client.getListWithLocks(url, getRevision(), SVNRevision.HEAD, false);
      List<RemoteResource> result = new ArrayList<RemoteResource>(list.length);

      // directories first
      for (ISVNDirEntryWithLock entryWithLock : list) {
        ISVNDirEntry entry = entryWithLock.getDirEntry();
        if (entry.getNodeKind() == SVNNodeKind.DIR) {
          result.add(
              new RemoteFolder(
                  this,
                  getRepository(),
                  url.appendPath(entry.getPath()),
                  getRevision(),
                  entry.getLastChangedRevision(),
                  entry.getLastChangedDate(),
                  entry.getLastCommitAuthor()));
        }
      }

      // files then
      for (ISVNDirEntryWithLock entryWithLock : list) {
        ISVNDirEntry entry = entryWithLock.getDirEntry();
        ISVNLock lock = entryWithLock.getLock();
        if (entry.getNodeKind() == SVNNodeKind.FILE) {
          RemoteFile remoteFile =
              new RemoteFile(
                  this,
                  getRepository(),
                  url.appendPath(entry.getPath()),
                  getRevision(),
                  entry.getLastChangedRevision(),
                  entry.getLastChangedDate(),
                  entry.getLastCommitAuthor());
          remoteFile.setPegRevision(getRevision());
          remoteFile.setLock(lock);
          result.add(remoteFile);
        }
      }

      // Save it to the cache
      children = (ISVNRemoteResource[]) result.toArray(new ISVNRemoteResource[result.size()]);
      return children;
    } catch (SVNClientException e) {
      throw new SVNException(
          new SVNStatus(
              IStatus.ERROR,
              SVNStatus.DOES_NOT_EXIST,
              Policy.bind(
                  "RemoteFolder.doesNotExist", getRepositoryRelativePath()))); // $NON-NLS-1$
    } finally {
      getRepository().returnSVNClient(client);
      progress.done();
    }
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNFolder#members(org.eclipse.core.runtime.IProgressMonitor, int)
   */
  public ISVNResource[] members(IProgressMonitor monitor, int flags) throws SVNException {
    final List<ISVNResource> result = new ArrayList<ISVNResource>();
    ISVNRemoteResource[] resources = getMembers(monitor);

    // RemoteFolders never have phantom members
    if ((flags & EXISTING_MEMBERS) == 0 && (flags & PHANTOM_MEMBERS) == 1) {
      return new ISVNResource[0];
    }
    boolean includeFiles =
        (((flags & FILE_MEMBERS) != 0) || ((flags & (FILE_MEMBERS | FOLDER_MEMBERS)) == 0));
    boolean includeFolders =
        (((flags & FOLDER_MEMBERS) != 0) || ((flags & (FILE_MEMBERS | FOLDER_MEMBERS)) == 0));
    boolean includeManaged =
        (((flags & MANAGED_MEMBERS) != 0)
            || ((flags & (MANAGED_MEMBERS | UNMANAGED_MEMBERS | IGNORED_MEMBERS)) == 0));

    for (ISVNResource svnResource : resources) {
      if ((includeFiles && (!svnResource.isFolder()))
          || (includeFolders && (svnResource.isFolder()))) {
        if (includeManaged) {
          result.add(svnResource);
        }
      }
    }
    return (ISVNResource[]) result.toArray(new ISVNResource[result.size()]);
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNResource#isFolder()
   */
  public boolean isFolder() {
    return true;
  }

  /* (non-Javadoc)
   * @see org.eclipse.team.core.variants.IResourceVariant#isContainer()
   */
  public boolean isContainer() {
    return true;
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNRemoteResource#members(org.eclipse.core.runtime.IProgressMonitor)
   */
  public ISVNRemoteResource[] members(IProgressMonitor progress) throws TeamException {
    return getMembers(progress);
  }

  /* (non-Javadoc)
   * @see org.tigris.subversion.subclipse.core.ISVNRemoteFolder#createRemoteFolder(java.lang.String, java.lang.String, org.eclipse.core.runtime.IProgressMonitor)
   */
  public void createRemoteFolder(String folderName, String message, IProgressMonitor monitor)
      throws SVNException {
    IProgressMonitor progress = Policy.monitorFor(monitor);
    progress.beginTask(Policy.bind("RemoteFolder.createRemoteFolder"), 100); // $NON-NLS-1$
    ISVNClientAdapter svnClient = null;
    try {
      svnClient = getRepository().getSVNClient();
      svnClient.mkdir(getUrl().appendPath(folderName), message);
      refresh();
      SVNProviderPlugin.getPlugin()
          .getRepositoryResourcesManager()
          .remoteResourceCreated(this, folderName);
    } catch (SVNClientException e) {
      throw SVNException.wrapException(e);
    } finally {
      getRepository().returnSVNClient(svnClient);
      progress.done();
    }
  }

  /* (non-Javadoc)
   * @see org.eclipse.team.core.variants.CachedResourceVariant#fetchContents(org.eclipse.core.runtime.IProgressMonitor)
   */
  protected void fetchContents(IProgressMonitor monitor) {
    // Do nothing. Folders do not have contents
  }
}
